package network.protocol;
import java.util.Vector;

import network.NetworkConstants;
import network.RSocket;
import network.byteConversions.ByteConverter;
import network.dataFormats.NetworkData;
import network.dataFormats.NetworkPackage;
import network.dataFormats.PackageType;
import network.serializing.SerializeInit;
import network.synchronizing.SyncDataReader;

import game.Game;
/**
 * Sends and receives data for the network client and updates the game state. Is also needed to create the initial game whenever a client is created.
 * 
 * @author toni
 * 
 */
public class ClientHandler extends NetworkHandler {
	private VirtualServer server;
	private FPSHandler fpsHandler;
	private PackageReader packageReader;
	public ClientHandler() {
		fpsHandler = new FPSHandler();
		packageReader = new PackageReader(this, game);
	}
	@Override
	public void handle() {
		Vector<NetworkData> outgoing = makeNetworkData(createData(server));
		Vector<NetworkData> incoming = server.receiveData();
		server.sendData(outgoing);
		processData(incoming);
	}
	private void processData(Vector<NetworkData> incoming) {
		Vector<NetworkPackage> packages = makeNetworkPackages(incoming);
		processAddRemove(packages);
		processSync(packages);
	}
	private void processSync(Vector<NetworkPackage> packages) {
		SyncDataReader.synchronize(packages, game);
	}
	private void processAddRemove(Vector<NetworkPackage> packages) {
		for (NetworkPackage p : packages) {
			// if (p.getType() != PackageType.TIMESYNC && p.getType() !=
			// PackageType.SYNCB) Help.p(p);
			switch (p.getType()) {
				case ADDB :
					packageReader.addBodyFromPackage(p);
					break;
				case REMB :
					packageReader.remBodyFromPackage(p);
					break;
				case ADDJ :
					packageReader.addJointFromPackage(p);
					break;
				case REMJ :
					packageReader.remJointFromPackage(p);
					break;
				case ADDG :
					packageReader.addGraphicsObjectFromPackage(p);
					break;
				case REMG :
					packageReader.remGraphicsObjectFromPackage(p);
					break;
				case ADDGO :
					packageReader.addGameObjectFromPackage(p);
					break;
				case REMGO :
					packageReader.remGameObjectFromPackage(p);
					break;
				case ADDBTOGO :
					packageReader.addBodyToGameObject(p);
					break;
				case ADDJTOGO :
					packageReader.addJointToGameObject(p);
					break;
				case ADDGROTOGO :
					packageReader.addGraphicsObjectToGameObject(p);
					break;
				case TIMESYNC :
					fpsHandler.handle(p, game.time, getCurrentFps());
					break;
				case ADDP :
					game.addPlayer(ByteConverter.bytesToInt(p.getData()));
					break;
				case CONTROL :
					game.controlHandling.handleControlData(p);
			}
		}
	}
	private Vector<NetworkPackage> createData(VirtualServer c) {
		Vector<NetworkPackage> packages = new Vector<NetworkPackage>();
		createControlData(packages);
		return packages;
	}
	private void createControlData(Vector<NetworkPackage> packages) {
		NetworkPackage p = game.controlHandling.getControlData(getNextId(), game.time);
		if (p != null)
			packages.add(p);
	}
	private void sendSinglePackage(NetworkPackage request) {
		NetworkData requestData = new NetworkData(request);
		Vector<NetworkData> allData = new Vector<NetworkData>();
		allData.add(requestData);
		server.sendData(allData);
	}
	public void setServer(RSocket s) {
		server = new VirtualServer(s);
	}
	@Override
	public int getWantedFps() {
		return fpsHandler.getWantedFps();
	}
	@Override
	public void setGame(Game g) {
		super.setGame(g);
		packageReader.setGame(g);
	}
	public void initialize() {
		long lastTime = 0;
		while (true) {
			// Request an initialization if a certain time interval has passed
			if (System.currentTimeMillis() - lastTime > NetworkConstants.INITINTERVAL) {
				NetworkPackage request = new NetworkPackage(PackageType.REQUESTINIT, new byte[0], getNextId(), 0.0f);
				sendSinglePackage(request);
				lastTime = System.currentTimeMillis();
			}
			// Look for incoming initialization-packages:
			Vector<NetworkData> data = server.receiveData();
			Vector<NetworkPackage> packages = makeNetworkPackages(data);
			for (NetworkPackage p : packages) {
				if (p.getType() == PackageType.INIT) { // Found the correct package, initialize and leave.
					NetworkInit init = SerializeInit.fromData(p.getData());
					NetworkPackage ack = new NetworkPackage(PackageType.GOTINIT, new byte[0], getNextId(), 0.0f);
					sendSinglePackage(ack);
					clear();
					setGame(init.game);
					init.game.setNetworkHandler(this);
					this.id = init.id;
					return;
				}
				else {
					pushBack(p);
				}
			}
		}
	}
	public Game getGame() {
		return game;
	}
}
